package it.uniroma2.wsdl;

import groovy.xml.MarkupBuilder;
import groovy.xml.QName;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Vector;

import com.predic8.schema.ComplexType;
import com.predic8.schema.Element;
import com.predic8.schema.Schema;
import com.predic8.wsdl.Binding;
import com.predic8.wsdl.BindingOperation;
import com.predic8.wsdl.Definitions;
import com.predic8.wsdl.Import;
import com.predic8.wsdl.Message;
import com.predic8.wsdl.Operation;
import com.predic8.wsdl.Part;
import com.predic8.wsdl.Port;
import com.predic8.wsdl.PortType;
import com.predic8.wsdl.Service;
import com.predic8.wsdl.creator.WSDLCreator;
import com.predic8.wsdl.creator.WSDLCreatorContext;
import com.predic8.wsdl.soap11.SOAPBinding;

public class WSDL_BPEL {
	private final String namespacePartnerLink = "http://docs.oasis-open.org/wsbpel/2.0/plnktype";
	private final String compatPartnerLink = "plnk";
	private final String compatvprop = "vprop";
	public static final String partMessage = "payload";
	public static final String messageInput = "MessageRequest";
	public static final String messageOutput = "MessageResponse";
	public static final String SERVICE = "Service";
	private static final String RESULT_ELEMENT = "result";
	private static final String INPUT_ELEMENT = "input";
	private final String soapBindingTransport = "http://schemas.xmlsoap.org/soap/http";
	private final String vpropNamespace = "http://docs.oasis-open.org/wsbpel/2.0/varprop";
	private final String xmlLine = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
	private final String compatDefault = "exwsdl";
	private static int countImport = 1;
	
	private Definitions wsdl;
	private Vector<PartnerLinkType_WSDL> partnerLink;
	private Hashtable<String, String> namespaces;
	
	
	public WSDL_BPEL(String name, String namespace){
		this.wsdl = new Definitions(namespace, name);
		this.setNamespace();
		this.partnerLink = new Vector<PartnerLinkType_WSDL>();
		this.namespaces = new Hashtable<String, String>();
	}

	
	public String dumpWSDL() {
		StringWriter writer = new StringWriter();
		WSDLCreator creator = new WSDLCreator();
		creator.setBuilder(new MarkupBuilder(writer));
		wsdl.create(creator, new WSDLCreatorContext());
		String finalWSDL = this.addPartnerLinkToWSDL(writer.toString());
		return finalWSDL;
	}
	
	
	private String addPartnerLinkToWSDL(String wsdl){
		int posStart = wsdl.indexOf("<definitions");
		int index = posStart;
		while(wsdl.charAt(index) != '>'){
			index++;
		}
		index++;
		String newWSDL = this.xmlLine + "\n" + wsdl.substring(0, index) + "\n" + this.createPartnerLinkType() + wsdl.substring(index);
		
		return newWSDL;
	}
	
	
	public void setNamespace(){
		((LinkedHashMap)this.wsdl.getNamespaceContext()).put(compatPartnerLink, namespacePartnerLink);
		((LinkedHashMap)this.wsdl.getNamespaceContext()).put(compatvprop, vpropNamespace);
	}
	
	public void addNamespace(String compat, String uri){
		((LinkedHashMap)this.wsdl.getNamespaceContext()).put(compat, uri);
	}
	
	
	public void createWSDL(){
		this.createType();
		this.createPortType();
		this.createService();
	}
	
	
	public void createType(){
		Schema schema = new Schema();
		schema.setTargetNamespace(this.wsdl.getTargetNamespace());
		schema.setElementFormDefault("qualified");
		
		Element elemTop1 = schema.newElement(this.wsdl.getName() + "Request");
		ComplexType ctTop1 = elemTop1.newComplexType();
		Element elemIn = ctTop1.newSequence().newElement(WSDL_BPEL.INPUT_ELEMENT);
		elemIn.setType(new QName(Schema.SCHEMA_NS, "string"));
		
		Element elemTop2 = schema.newElement(this.wsdl.getName() + "Response");
		ComplexType ctTop2 = elemTop2.newComplexType();
		Element elemOut = ctTop2.newSequence().newElement(WSDL_BPEL.RESULT_ELEMENT);
		elemOut.setType(new QName(Schema.SCHEMA_NS, "string"));
		
		this.wsdl.add(schema);
	}
	
	
//	public void createMessage(){
//		Message msg = this.wsdl.newMessage(this.wsdl.getName() + WSDL_BPEL.messageInput);
//		Part partMsg = msg.newPart("payload", "tes");
//		partMsg.setType(new QName(Schema.SCHEMA_NS, "string"));
//		
//		Message msgOut = this.wsdl.newMessage(this.wsdl.getName() + WSDL_BPEL.messageOutput);
//		Part partMsgOut = msgOut.newPart("payload", "test");
//		partMsgOut.setType(new QName(Schema.SCHEMA_NS, "string"));
//		
//	}
	
	
	public void createPortType(){
		PortType portType = this.wsdl.newPortType(this.wsdl.getName());
		Operation op = portType.newOperation("process");
		op.newInput("").newMessage(this.wsdl.getName() + this.messageInput).newPart(this.partMessage, "tns:" + this.wsdl.getName() + "Request");
		op.newOutput("").newMessage(this.wsdl.getName() + this.messageOutput).newPart(this.partMessage, "tns:" + this.wsdl.getName() + "Response");
		
	}
	
	
	public void createService(){
		Service service = this.wsdl.newService(this.wsdl.getName() + WSDL_BPEL.SERVICE);
		Port port = service.newPort(this.wsdl.getName()+"Port");
		
		Binding bnd = port.newBinding(this.wsdl.getName() + "Binding");
	    bnd.setType(this.wsdl.getPortType(this.wsdl.getName()));
	    bnd.newSOAP11Binding();
	    
	    BindingOperation bo = bnd.newBindingOperation("process");
	    bo.newSOAP11Operation().setSoapAction(this.wsdl.getTargetNamespace() + "/process");
	    bo.newInput().newSOAP11Body();
	    bo.newOutput().newSOAP11Body();
	    
	    port.newSOAP11Address("http://localhost:8080/ode/processes/" + this.wsdl.getName());

	}
	
	
	public void createBinding(Port port){
		Binding binding = this.wsdl.newBinding(this.wsdl.getName() + "Binding");
		SOAPBinding soapBin = binding.newSOAP11Binding();
		soapBin.setStyle("document");
		soapBin.setTransport(this.soapBindingTransport);
		BindingOperation binOp = binding.newBindingOperation("process");
		binOp.newSOAP11Operation().setSoapAction("http://ws.invocation.flow_if_invoke/process");
		binOp.newInput().newSOAP11Body().setUse("literal");
		binOp.newOutput().newSOAP11Body().setUse("literal");
	}
	
	
	public String createPartnerLinkType(){
		String output = "";
		for(PartnerLinkType_WSDL plt : this.partnerLink){
			output = output + plt.toWSDL();
		}
		return output;
	}
	
	
	public void addPartnerLinkType(String namePLT, String nameRole, String portType, String namespace, String location){
		String newCompat = this.compatDefault + WSDL_BPEL.countImport;
		WSDL_BPEL.countImport++;
		this.namespaces.put(namespace, newCompat);
		this.addNamespace(newCompat, namespace);
		if(!this.wsdl.getTargetNamespace().equals(namespace)){
			this.createImport(location, namespace);
		}
		this.partnerLink.addElement(new PartnerLinkType_WSDL(namePLT, nameRole, newCompat + ":" + portType));
		
		
		
	}
	
	
	public void createImport(String location, String namespace){
		Import importWSDl = new Import();
		importWSDl.setLocation(location);
		importWSDl.setNamespace(namespace);
		List<Import> importWSDL = this.wsdl.getImports();
		if(importWSDL != null && importWSDL.size()>0){
			importWSDL.add(importWSDl);
			this.wsdl.setImports(importWSDL);
		}else{
			List<Import> listImport = new ArrayList<Import>();
			listImport.add(importWSDl);
			this.wsdl.setImports(listImport);
		}
	}
}